import {
  BadRequestException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { UpdateScriptListDto } from './dto/update-script-list.dto';
import { CreateScriptListDto } from './dto/create-script-list.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { ScriptList } from './entities/script-list.entity';
import { GroupToScript } from './entities/grouptoscript.entity';
import { CreateGroupToScriptListDto } from './dto/create-grouptoscript.dto';
import { User } from 'src/user/entities/user.entity';
import { usertogroup } from 'src/group/entities/usertogroup.entity';
import { UpdateGroupToScriptListDto } from './dto/upadte-grouptoscript.dto';
import { SceneListService } from 'src/scene-list/scene-list.service';
import { PathListService } from 'src/path-list/path-list.service';
import { ScenePathRelationalService } from 'src/scene_path_relational/scene_path_relational.service';
import { SceneFileRelationalService } from 'src/scene_file_relational/scene_file_relational.service';
import { PathItemRelationalService } from 'src/path-item-relational/path-item-relational.service';
import { CameraRotateService } from 'src/camera-rotate/camera-rotate.service';
import { AudioService } from 'src/audio/audio.service';
import { TextService } from 'src/text/text.service';
import { PictureService } from 'src/picture/picture.service';
import { VideoService } from 'src/video/video.service';
import { CameraalongService } from 'src/cameraalong/cameraalong.service';
import { DynamiclayerService } from 'src/dynamiclayer/dynamiclayer.service';
import { GeojsonService } from 'src/geojson/geojson.service';
import { ImagerasterService } from 'src/imageraster/imageraster.service';
import { MapEaseService } from 'src/map-ease/map-ease.service';
import { MarkerService } from 'src/marker/marker.service';
import { CreateSceneListDto } from 'src/scene-list/dto/create-scene-list.dto';
import { SceneBondScriptService } from 'src/scene_bond_script/scene_bond_script.service';

@Injectable()
export class ScriptListService {
  constructor(
    @InjectRepository(ScriptList)
    private readonly scriptListRepository: Repository<ScriptList>,
    @InjectRepository(GroupToScript)
    private readonly groupToScriptRepository: Repository<GroupToScript>,
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
    @InjectRepository(usertogroup)
    private readonly usertogroupRepository: Repository<usertogroup>,
    private readonly sceneListService: SceneListService,
    private readonly pathListService: PathListService,
    private readonly scenePathRelationalService: ScenePathRelationalService,
    private readonly sceneFileRelationalService: SceneFileRelationalService,
    private readonly pathItemRelationalService: PathItemRelationalService,
    private readonly cameraRotateService: CameraRotateService,
    private readonly audioService: AudioService,
    private readonly textService: TextService,
    private readonly videoService: VideoService,
    private readonly pictureService: PictureService,
    private readonly cameraalongService: CameraalongService,
    private readonly dynamiclayerService: DynamiclayerService,
    private readonly geojsonService: GeojsonService,
    private readonly imagerasterService: ImagerasterService,
    private readonly mapEaseService: MapEaseService,
    private readonly markerService: MarkerService,
    private readonly sceneBondScriptService: SceneBondScriptService,
  ) {}
  async createScript(createScriptListDto: CreateScriptListDto) {
    const insertResearch = await this.scriptListRepository.insert(
      this.scriptListRepository.create(createScriptListDto),
    );
    return insertResearch.raw[0].id;
  }
  getAllScripts(): Promise<ScriptList[]> {
    return this.scriptListRepository.find();
  }
  getScriptItemById(id: string): Promise<ScriptList> {
    return this.scriptListRepository.findOne({ where: { id: id } });
  }
  async checkScriptById(id: string) {
    const res = await this.getScriptItemById(id);
    if (res === null) {
      return false;
    } else {
      return true;
    }
  }
  getAllScriptItemByUser(email: any) {
    // 设计到group概念之后再次进行修改
    return this.scriptListRepository.find({
      where: { creator: email, role: 'person' },
    });
  }
  async updateScriptById(id: string, updateScriptListDto: UpdateScriptListDto) {
    const result = await this.scriptListRepository.update(
      id,
      updateScriptListDto,
    );
    if (result.affected === 0) {
      throw new NotFoundException(`Picture with ID "${id}" not found.`);
    }
  }
  removeScriptById(id: string) {
    try {
      return this.scriptListRepository.delete({ id: id });
    } catch (error) {
      return `删除失败`;
    }
  }
  async isScriptItemExist(scriptId: string) {
    try {
      await this.scriptListRepository.findOne({
        where: { id: scriptId },
      });
      return true;
    } catch {
      return false;
    }
  }

  async createScriptItemToGroup(
    createScriptListDto: CreateScriptListDto,
    groupuuid: string,
  ) {
    const scriptuuid = await this.createScript(createScriptListDto);
    const createGroupToScriptListDto = new CreateGroupToScriptListDto();
    createGroupToScriptListDto.belong = createScriptListDto.creator;
    createGroupToScriptListDto.scriptuuid = scriptuuid;
    createGroupToScriptListDto.groupuuid = groupuuid;
    this.groupToScriptRepository.insert(
      this.groupToScriptRepository.create(createGroupToScriptListDto),
    );
    return scriptuuid;
  }

  async getScriptItemByGroupuuid(groupuuid: string) {
    const scriptItemList = await this.groupToScriptRepository.find({
      where: { groupuuid: groupuuid },
    });

    const scriptItemOfGroupList = await Promise.all(
      scriptItemList.map(async (item) => {
        const scriptInfo = await this.scriptListRepository.find({
          where: { id: item.scriptuuid },
        });

        if (scriptInfo.length !== 0) {
          const userItem = await this.userRepository.findOne({
            where: { email: item.belong },
          });

          const usertogroupItem = await this.usertogroupRepository.findOne({
            where: { useruuid: userItem.id, groupuuid: groupuuid },
          });

          (scriptInfo[0] as any).creator_nickname =
            usertogroupItem.groupnickname;
          (scriptInfo[0] as any).scenceToGroupId = item.id;
        }

        return scriptInfo[0];
      }),
    );

    return scriptItemOfGroupList;
  }
  async removeScriptItemFromGroup(scriptTogroupuuid: string) {
    try {
      const scriptTogroupInfo = await this.groupToScriptRepository.findOne({
        where: { id: scriptTogroupuuid },
      });
      await this.removeScriptById(scriptTogroupInfo.scriptuuid);
      return this.groupToScriptRepository.delete({
        id: scriptTogroupuuid,
      });
    } catch (err) {
      console.error('删除记录失败', err);
      throw '删除记录失败';
    }
  }

  async deleteScriptItemFromGroup(scriptuuid: string) {
    try {
      await this.removeScriptById(scriptuuid);
      return this.groupToScriptRepository.delete({
        id: scriptuuid,
      });
    } catch (err) {
      console.error('删除记录失败', err);
      throw '删除记录失败';
    }
  }

  async updateScriptItemToGroup(
    scriptId: string,
    updateGroupToScriptListDto: UpdateGroupToScriptListDto,
  ) {
    const oldItem = await this.groupToScriptRepository.findOne({
      where: { scriptuuid: scriptId },
    });
    if (!oldItem) {
      return '该记录不存在';
    }
    try {
      const updateItem = this.groupToScriptRepository.merge(
        oldItem,
        updateGroupToScriptListDto,
      );
      await this.groupToScriptRepository.save(updateItem);
      return '修改记录成功!';
    } catch (err) {
      throw '更新记录失败';
    }
  }

  async unlockAllScripts(email: any) {
    const lockedItems = await this.groupToScriptRepository.find({
      where: { locked_by: email },
    });
    if (lockedItems.length === 0) {
      return;
    }
    const updatePromises = lockedItems.map((item) => {
      return this.updateScriptItemToGroup(item.scriptuuid, {
        locked: false,
        locked_by: '',
        lock_timestamp: new Date(),
      });
    });

    await Promise.all(updatePromises);
  }

  getGroupToScriptItem(scriptId: string) {
    return this.groupToScriptRepository.findOne({
      where: { scriptuuid: scriptId },
    });
  }

  async transformScriptToScence(scriptId: string) {
    // 判断当前脚本是否关联场景
    const findres =
      await this.sceneBondScriptService.getBondByScriptuuid(scriptId);
    if (findres) {
      await this.sceneBondScriptService.deleteBond(findres.id);
      await this.sceneListService.remove(findres.scene_id);
    }
    try {
      const scriptItem = await this.getScriptItemById(scriptId);
      // // 个人场景 转换方式
      const scenceId = await this.createScenceByScript(scriptItem);
      const newShots = await this.clearShots(scriptItem.shots);
      const pathsList = await this.createPathsByShots(
        newShots[0],
        scriptItem.creator,
        scenceId,
      );
      await this.createScenceItemByShots(
        pathsList,
        newShots,
        scenceId,
        scriptItem.creator,
      );
      // 生成
      await this.sceneBondScriptService.createBond({
        scene_id: scenceId,
        script_id: scriptId,
      });
      return scenceId;
    } catch (err) {
      console.log(err);
      throw new BadRequestException('转换失败');
    }
  }

  async createScenceItemByShots(
    path_listUuid: object,
    shots: Array<any>,
    scenceuuid: string,
    creator: string,
  ) {
    for (const shot of shots) {
      const keys = Object.keys(shot);
      for (const [key] of Object.entries(shot)) {
        if (key.startsWith('subtitles') || key.startsWith('text')) {
          if (shot[key].content !== '') {
            const itemuuid = await this.textService.createForScript({
              ...shot[key],
              creator: creator,
            });
            await this.pathItemRelationalService.create({
              path_id: path_listUuid['Text'],
              item_id: itemuuid,
              type: 'Text',
            });
          }
        } else if (key.startsWith('video')) {
          if (shot[key].file_id !== '') {
            const itemuuid = await this.videoService.createForScript({
              ...shot[key],
              creator: creator,
            });
            await this.pathItemRelationalService.create({
              path_id: path_listUuid['Video'],
              item_id: itemuuid,
              type: 'Video',
            });
            await this.sceneFileRelationalService.create({
              scene_id: scenceuuid,
              file_id: shot[key].file_id,
              used: true,
            });
          }
        } else if (key.startsWith('audio')) {
          if (shot[key].file_id !== '') {
            const itemuuid = await this.audioService.createForScript({
              ...shot[key],
              creator: creator,
            });
            await this.pathItemRelationalService.create({
              path_id: path_listUuid['Audio'],
              item_id: itemuuid,
              type: 'Audio',
            });
            await this.sceneFileRelationalService.create({
              scene_id: scenceuuid,
              file_id: shot[key].file_id,
              used: true,
            });
          }
        } else if (key.startsWith('picture')) {
          if (shot[key].file_id !== '') {
            const itemuuid = await this.pictureService.createForScript({
              ...shot[key],
              creator: creator,
            });
            await this.pathItemRelationalService.create({
              path_id: path_listUuid['Picture'],
              item_id: itemuuid,
              type: 'Picture',
            });
            await this.sceneFileRelationalService.create({
              scene_id: scenceuuid,
              file_id: shot[key].file_id,
              used: true,
            });
          }
        } else if (key.startsWith('imageraster')) {
          if (shot[key].file_id !== '') {
            const itemuuid = await this.imagerasterService.createForScript({
              ...shot[key],
              creator: creator,
            });
            await this.pathItemRelationalService.create({
              path_id: path_listUuid['ImageRaster'],
              item_id: itemuuid,
              type: 'ImageRaster',
            });
            await this.sceneFileRelationalService.create({
              scene_id: scenceuuid,
              file_id: shot[key].file_id,
              used: true,
            });
          }
        } else if (key.startsWith('geojson')) {
          if (shot[key].file_id !== '') {
            const itemuuid = await this.geojsonService.createItemForScript({
              ...shot[key],
              creator: creator,
            });
            await this.pathItemRelationalService.create({
              path_id: path_listUuid['GeoJson'],
              item_id: itemuuid,
              type: 'GeoJson',
            });
            await this.sceneFileRelationalService.create({
              scene_id: scenceuuid,
              file_id: shot[key].file_id,
              used: true,
            });
          }
        } else if (key.startsWith('marker')) {
          if (shot[key].file_id !== '' && shot[key].features !== '') {
            const itemuuid = await this.markerService.createMarkerItemForScript(
              { ...shot[key], creator: creator },
            );
            await this.pathItemRelationalService.create({
              path_id: path_listUuid['Marker'],
              item_id: itemuuid,
              type: 'Marker',
            });
            await this.sceneFileRelationalService.create({
              scene_id: scenceuuid,
              file_id: shot[key].file_id,
              used: true,
            });
          }
        } else if (key.startsWith('dynamiclayer')) {
          if (shot[key].file_id !== '') {
            const itemuuid = await this.dynamiclayerService.createForScript({
              ...shot[key],
              creator: creator,
            });
            await this.pathItemRelationalService.create({
              path_id: path_listUuid['DynamicLayer'],
              item_id: itemuuid,
              type: 'DynamicLayer',
            });
            await this.sceneFileRelationalService.create({
              scene_id: scenceuuid,
              file_id: shot[key].file_id,
              used: true,
            });
          }
        } else if (key.startsWith('camerarotate')) {
          if (JSON.stringify(shot[key]) !== '{}') {
            const itemuuid =
              await this.cameraRotateService.createCameraRotateItemForScript({
                ...shot[key],
                creator: creator,
              });
            await this.pathItemRelationalService.create({
              path_id: path_listUuid['CameraRotate'],
              item_id: itemuuid,
              type: 'CameraRotate',
            });
          }
        } else if (key.startsWith('mapease')) {
          if (JSON.stringify(shot[key]) !== '{}') {
            const itemuuid =
              await this.mapEaseService.createMapEaseItemForScript({
                ...shot[key],
                creator: creator,
              });
            await this.pathItemRelationalService.create({
              path_id: path_listUuid['MapEase'],
              item_id: itemuuid,
              type: 'MapEase',
            });
          }
        } else if (key.startsWith('cameraalong')) {
          if (JSON.stringify(shot[key]) !== '{}') {
            const itemuuid = await this.cameraalongService.createForScript({
              ...shot[key],
              creator: creator,
            });
            await this.pathItemRelationalService.create({
              path_id: path_listUuid['CameraAlong'],
              item_id: itemuuid,
              type: 'CameraAlong',
            });
            await this.sceneFileRelationalService.create({
              scene_id: scenceuuid,
              file_id: shot[key].file_id,
              used: true,
            });
          }
        }
      }
    }
  }

  async clearShots(shots: Array<any>) {
    const newShots = [];
    for (const shot of shots) {
      if (shot.viewChange && shot.viewChange.selected !== 'none') {
        const selectedKey = shot.viewChange.selected;
        shot[selectedKey] = shot.viewChange[selectedKey];
      }

      // 确保属性存在
      shot.mapease = shot.mapease || {};
      shot.camerarotate = shot.camerarotate || {};
      shot.cameraalong = shot.cameraalong || {};

      // 删除不需要的属性
      delete shot.viewChange;
      delete shot.key;
      delete shot.outline;
      delete shot.descript;
      delete shot.remark;
      delete shot.time_range;
      delete shot.checked;
      delete shot.isMerged;

      // 去除空对象属性
      const filteredShot = Object.fromEntries(
        Object.entries(shot).filter(
          ([, value]) =>
            !(
              value &&
              typeof value === 'object' &&
              Object.keys(value).length === 0
            ),
        ),
      );

      // 对属性排序并推入新数组
      newShots.push(
        Object.fromEntries(
          Object.entries(filteredShot).sort(
            ([a], [b]) => a.charCodeAt(0) - b.charCodeAt(0),
          ),
        ),
      );
    }

    return newShots;
  }

  async createScenceByScript(scriptItem: any) {
    const scenceDto = new CreateSceneListDto();
    scenceDto.creator = scriptItem.creator;
    scenceDto.description = scriptItem.title;
    scenceDto.duration = 5000;
    scenceDto.role = scriptItem.role;
    scenceDto.share = 0;
    scenceDto.stage_title = this.getStageTitleArray(scriptItem.shots);
    return await this.sceneListService.create(scenceDto, false);
  }

  getStageTitleArray(shots) {
    const result = [];
    const mergedMap = new Map(); // 存储按 isMerged 分组的数据
    let stageCount = 1; // 阶段计数器

    shots.forEach((item) => {
      if (
        item.time_range &&
        item.time_range.start !== undefined &&
        item.time_range.finish !== undefined
      ) {
        const isMergedKey = item.isMerged || false; // 使用 isMerged 作为分组键

        if (!mergedMap.has(isMergedKey)) {
          mergedMap.set(isMergedKey, {
            key: stageCount - 1,
            name: `阶段${stageCount}`, // 按顺序命名阶段
            title: item.outline || '', // 使用 item.outline 作为标题
            start: item.time_range.start,
            end: item.time_range.finish,
          });
          stageCount++; // 更新阶段计数
        } else {
          const existing = mergedMap.get(isMergedKey);
          existing.start = Math.min(existing.start, item.time_range.start); // 更新最小开始时间
          existing.end = Math.max(existing.end, item.time_range.finish); // 更新最大结束时间
        }
      }
    });

    // 将合并后的阶段加入结果数组
    result.push(...mergedMap.values());
    return result;
  }

  async createPathsByShots(obj: object, creator: string, scenceuuid: string) {
    const pathsuuid = {};
    const createPath = async (type) => {
      const pathuuid = await this.pathListService.create({
        creator: creator,
        ord: 7,
        type: type,
        visual: true,
      });
      await this.scenePathRelationalService.create({
        scene_id: scenceuuid,
        path_id: pathuuid,
      });
      pathsuuid[type] = pathuuid;
    };

    for (const [key] of Object.entries(obj)) {
      if (key.startsWith('subtitles') || key.startsWith('text')) {
        await createPath('Text');
      } else if (key.startsWith('video')) {
        await createPath('Video');
      } else if (key.startsWith('audio')) {
        await createPath('Audio');
      } else if (key.startsWith('picture')) {
        await createPath('Picture');
      } else if (key.startsWith('imageraster')) {
        await createPath('ImageRaster');
      } else if (key.startsWith('geojson')) {
        await createPath('GeoJson');
      } else if (key.startsWith('marker')) {
        await createPath('Marker');
      } else if (key.startsWith('dynamiclayer')) {
        await createPath('DynamicLayer');
      } else if (key.startsWith('camerarotate')) {
        await createPath('CameraRotate');
      } else if (key.startsWith('mapease')) {
        await createPath('MapEase');
      } else if (key.startsWith('cameraalong')) {
        await createPath('CameraAlong');
      }
    }
    return pathsuuid;
  }

  getAllPublicScriptItem() {
    return this.scriptListRepository.find({
      where: { script_type: 'public' },
    });
  }
}
